home *** CD-ROM | disk | FTP | other *** search
- /* $Id: lgd.c,v 1.7 89/09/20 17:49:12 mbp Exp $
- *
- * lgd.c: top-level LGD procedures
- */
-
- /************************************************************************
- * Copyright (C) 1989 by Mark B. Phillips *
- * *
- * Permission to use, copy, modify, and distribute this software and *
- * its documentation for any purpose and without fee is hereby granted, *
- * provided that the above copyright notice appear in all copies and *
- * that both that copyright notice and this permission notice appear in *
- * supporting documentation, and that the name of Mark B. Phillips or *
- * the University of Maryland not be used in advertising or publicity *
- * pertaining to distribution of the software without specific, written *
- * prior permission. This software is provided "as is" without express *
- * or implied warranty. *
- ************************************************************************/
-
- #include <stdio.h>
- #include "lgd.h"
- #include "internal.h"
- #include "mf.h"
- #include "vector.h"
- #include "dpu.h"
-
- /************************************************************************
- * PUBLIC DEFINITIONS *
- ************************************************************************/
-
- /* Current dimension (always 3 for now): */
- int lgd_dim=3;
-
- /* Length of segment headers: */
- int lgd_header_length=4;
-
- /* Length of segment trailers: */
- int lgd_trailer_length=1;
-
- /* Error message: */
- char *lgd_error=NULL;
-
- /* Format number for world files: */
- int lgd_world_file_fmt=3;
-
- /*
- * World file format history:
- * 1. The original version.
- * 2. Changed "lgd_save/lgd_load" to write out current segment number
- * instead of stack of current segments (segment nesting
- * eliminated). Also changed dpu.c to write out lgd_View3
- * structure instead of separate eye, focus, etc.
- * 3. Exactly the same as 2, but uses new lgd_View3 structure, which
- * has h1 and h2 fields now.
- */
-
- /* Tolerance, as fraction of screen width, for picking */
- #define PICK_TOLERANCE_DEFAULT 0.01
- double lgd_pick_tolerance = PICK_TOLERANCE_DEFAULT;
-
- /* Corners of world box (lgd_wbox[0]=low vals, lgd_wbox[1]=high vals): */
- double lgd_wbox[2][MAXDIM];
-
- /* Scaling factors used to convert to canonincal integer coords: */
- double lgd_wscale[MAXDIM];
-
- /*----------------------End of Public Definitions-----------------------*/
-
- /************************************************************************
- * PRIVATE DEFINITIONS *
- ************************************************************************/
-
- #define YES 1
- #define NO 0
-
- static int pen_status;
- #define UP 0
- #define DOWN 1
- #define PEN_IS_UP (!pen_status)
- #define PEN_IS_DOWN (pen_status)
-
- /* Current (legal) Point (takes clipping into account): */
- static double cp[MAXDIM];
-
- /* Current Real Point (does not take clipping into account): */
- static double crp[MAXDIM];
-
- /* Number ("name") for "no" segment: */
- #define NULL_SEGMENT ((mfe)0)
-
- /* Number of currently open segment: */
- static mfe cur_segment=NULL_SEGMENT;
-
- /* Tell whether there is a segment open: */
- #define SEGMENT_OPEN (cur_segment!=NULL_SEGMENT)
- #define NO_SEGMENT_OPEN (cur_segment==NULL_SEGMENT)
-
- /* Current drawing color and style: */
- static mfe cur_color;
- static mfe cur_style;
-
- /* Visibility flags: */
- #define VISIBLE 1
- #define INVISIBLE 0
-
- /* Internal functions: */
- static mfa segment_address();
- static mfe generate_unique_segment_number();
- static char *error_string();
-
- /* Error handling things: */
- static char *lgd_internal_error;
- #define MAKE_LGD_ERR(s,e) {lgd_error = error_string(s,e);e=NULL;}
- #define MAKE_LGD_INTERNAL_ERR(s,e) \
- {lgd_internal_error = error_string(s,e);e=NULL;}
-
- /*------------------End of Private Definitions--------------------------*/
-
- /************************************************************************
- * PUBLIC PROCEDURES *
- ************************************************************************/
-
- /*-----------------------------------------------------------------------
- * Function: lgd_initialize
- * Description: initialize LGD
- * Arguments: (none)
- * Returns: nothing
- * Notes: This procedure must be called before any other
- * LGD procedures.
- */
- lgd_initialize()
- {
- mf_initialize(); /* Initialize the metafile */
- if (mf_error!=NULL) {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- return;
- }
- dpu_initialize(); /* Initialize the dpu */
- if (dpu_error!=NULL) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- return;
- }
- pen_status = UP; /* Pen is initially UP */
- cur_color = LGD_WHITE; /* Color defaults to white */
- cur_style = 1; /* Line style defaults to 1 */
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_define_world
- * Description: defines the world coordinate system
- * Arguments IN: dimension: the dimension of the world
- * wbox_low: low corner of world
- * wbox_high: high corner of world
- * Returns: nothing
- */
- lgd_define_world( dimension, wbox_low, wbox_high )
- int dimension;
- double wbox_low[], wbox_high[];
- {
- int i;
- lgd_View3 view;
- /* check validity of dimension */
- if (dimension != 3) {
- lgd_error = E_bad_dim;
- return;
- }
- lgd_dim = dimension; /* Set global dim variable */
- for (i=0; i<lgd_dim; ++i) {
- /* check validity of coords */
- if (wbox_low[i]>wbox_high[i]) {
- lgd_error = E_bad_wld_bdy;
- return;
- }
- lgd_wbox[0][i] = wbox_low[i]; /* Set world */
- lgd_wbox[1][i] = wbox_high[i]; /* coordinate values */
- }
- for (i=0; i<lgd_dim; ++i) /* Set scale factors */
- lgd_wscale[i] = ( (double) MF_max_val )
- / (lgd_wbox[1][i]-lgd_wbox[0][i]);
- LGD_copy_vec( cp, lgd_wbox[0] ); /* Set default current */
- LGD_copy_vec( crp, lgd_wbox[0] ); /* points = lower corner */
- dpu_get_default_view( &view );
- dpu_set_view( &view );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_delete_all
- * Description: Delete the current picture completely
- * Arguments: (none)
- * Returns: nothing
- * Notes: This clears out the current picture, leaving the
- * current world definition and menu setting intact.
- *
- * NOTE: Right now I can't see that this proc should do
- * anything other than exactly what lgd_intialize does.
- * I'm making it a separate proc because it performs a
- * conceptually different task, though, and because at
- * some point in the future it and lgd_initialize may
- * need to perform different tasks.
- */
- lgd_delete_all()
- {
- lgd_initialize();
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_world
- * Description: Get the current world definition data
- * Arguments OUT: *dimension: the dimension of the world
- * wbox_low: lower corner of world
- * wbox_high: upper corner of world
- * Returns: nothing
- */
- lgd_inquire_world( dimension, wbox_low, wbox_high )
- int *dimension;
- double wbox_low[],wbox_high[];
- {
- *dimension = lgd_dim;
- if (lgd_dim!=0) {
- LGD_copy_vec( wbox_low, lgd_wbox[0] );
- LGD_copy_vec( wbox_high, lgd_wbox[1] );
- }
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_move_abs
- * Description: Move the current point to a new point
- * Arguments IN: v: the point to move to
- * Returns: nothing
- * Notes: This procedure does clipping
- */
- lgd_move_abs( v )
- double v[];
- {
- if (in_world_box(v)) move_abs(v);
- LGD_copy_vec( crp, v );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_move_rel
- * Description: Move the current point by relative displacement
- * Arguments IN: dv: the vector to add to current point
- * Returns: nothing
- * Notes: cp+dv MUST be inside (or on the boundary of) the world.
- * If it is not, an error is set and the current point
- * is left unchanged.
- */
- lgd_move_rel( dv )
- double dv[];
- {
- double v[MAXDIM];
-
- LGD_add_vec( v, crp, dv );
- lgd_move_abs( v );
- }
-
- /*--------------------------------------------------------------
- * Function: lgd_draw_abs
- * Description: Draw from the current point to a new point, and
- * then make that new point the current point
- * Arguments IN: v: the point to draw to
- * Returns: nothing
- * Notes: This procedures does clipping
- */
- lgd_draw_abs( v )
- double v[];
- {
- double p[MAXDIM];
- int clip1, visible;
-
- LGD_copy_vec(p, v);
- clipit(crp, p, &clip1, &visible);
- if (visible) {
- if (clip1) move_abs(crp);
- draw_abs(p);
- }
- LGD_copy_vec(crp, v);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_draw_rel
- * Description: Draw a line from current point to current point
- * plus displacement vector, leaving current point
- * at the final endpont of the line drawn.
- * Arguments IN: dv: the vector to add to current point
- * Returns: nothing
- * Notes: cp+dv MUST be inside (or on the boundary of) the world.
- * If it is not, an error is set and the current point
- * is left unchanged.
- */
- lgd_draw_rel( dv )
- double dv[];
- {
- double v[MAXDIM];
-
- LGD_add_vec( v, crp, dv );
- lgd_draw_abs( v );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_point
- * Description: Draw a point at the current point
- * Arguments: (none)
- * Returns: nothing
- * Notes: This only really draws a point if the current real
- * point is inside the world box. Otherwise, it does
- * nothing (the point is clipped).
- */
- lgd_point()
- {
- if NO_SEGMENT_OPEN {
- lgd_error = E_pt_noseg;
- return;
- }
- if (in_world_box(crp)) {
- mf_append( MF_point );
- if (mf_error==NULL)
- dpu_point();
- else
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_update_display
- * Description: Make sure the display is up to date
- * Arguments: (none)
- * Returns: nothing
- */
- lgd_update_display()
- {
- dpu_update_display();
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_begin_segment
- * Description: begin a new segment
- * Arguments OUT: *segment_number: the number of the new segment
- * Returns: nothing
- * Notes: Segment numbers are assigned automatically
- */
- lgd_begin_segment( segment_number )
- int *segment_number;
- {
- mfa save;
- mfe ivec[MAXDIM];
-
- if (SEGMENT_OPEN) { /* make sure there is no */
- lgd_error = E_seg_open; /* segment already open */
- return;
- } /* get number for new segment */
- cur_segment =
- (mfe)(*segment_number = generate_unique_segment_number());
- if (lgd_internal_error!=NULL) {
- MAKE_LGD_ERR("",lgd_internal_error);
- return;
- }
- save = mf_last_addr(); /* Save current end of MF */
- mf_append( MF_begin_segment ); /* BEGINNING OF HEADER */
- mf_append( cur_segment ); /* segment number */
- mf_append( (mfe)VISIBLE ); /* visibility */
- mf_append( (mfe) 0 ); /* length; will be set when */
- /* segment is closed) */
- /* END OF HEADER */
- /*
- * IMPORTANT NOTE: If the length of (number of words contained in)
- * the header is changed, the setting of the external variable
- * lgd_header_length (at the top of this file) should be changed
- * appropriately.
- */
-
- mf_append( MF_line_style ); /* Establish current line style */
- mf_append( cur_style );
- mf_append( MF_color ); /* Establish current color */
- mf_append( cur_color );
- mf_append( MF_pen_up ); /* Move to current point */
- can_int_coord( ivec, cp );
- mf_append_array( ivec, lgd_dim );
- if PEN_IS_DOWN /* Restore current pen status */
- mf_append( MF_pen_down );
- /* If any error has been encountered, restore the MF to the way it
- * was upon entry. */
- if (mf_error) {
- mf_truncate( save );
- cur_segment = NULL_SEGMENT;
- MAKE_LGD_ERR("(MetaFile) ", mf_error);
- }
- else {
- dpu_begin_segment( cur_segment );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_end_segment
- * Description: Ends the current segment
- * Arguments: (none)
- * Returns: nothing
- * Notes: Does nothing if there is no segment currently
- * open.
- */
- lgd_end_segment()
- {
- mfa seg_addr, seg_length;
-
- if (NO_SEGMENT_OPEN) { /* Make sure there is a seg open */
- lgd_error = E_cs_noseg;
- return;
- }
- mf_append( MF_end_segment ); /* BEGINNING OF TRAILER */
- /* END OF TRAILER */
- /*
- * IMPORTANT NOTE: If the length of (number of words contained in)
- * the trailer is changed, the setting of the external variable
- * lgd_trailer_length (at the top of this file) should be changed
- * appropriately.
- */
- if (mf_error) {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- return;
- }
- /* Now compute the length of the segment just closed and put it in
- * the header */
- seg_addr = segment_address( cur_segment );
- seg_length = mf_last_addr() - seg_addr + 1;
- if (seg_length > MAX_MFE_VAL) {
- lgd_error = E_seg_too_long;
- return;
- }
- mf_poke( (mfa)(seg_addr+LEN_OS), (mfe)seg_length );
- cur_segment = NULL_SEGMENT;
- dpu_end_segment();
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_delete_segment
- * Description: Delete a segment
- * Arguments IN: segment_number: number of segment to delete
- * Returns: nothing
- * Notes: Deleting the currently open segment is not
- * allowed. An attempt to do so causes an error.
- */
- lgd_delete_segment( segment_number )
- int segment_number;
- {
- mfa first, last;
-
- /* First make sure it's not the current segment */
- if ((mfe)segment_number==cur_segment) {
- lgd_error = E_del_open_seg;
- return;
- }
- /* Then find its address in the MF */
- first = segment_address( (mfe)(segment_number) );
- if (first==NULL_SEG_ADDRESS) {
- lgd_error = E_del_non_seg;
- return;
- }
- /* Tell the DPU to erase it */
- dpu_delete_segment( (mfe)(segment_number) );
- if (dpu_error!=NULL) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- return;
- }
- /* Find its end and take it out */
- last = first + mf_peek(first+LEN_OS) - 1;
- mf_compress( first, last );
- if (mf_error) {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
-
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_segment_visibility
- * Description: Set the visibility of a segment
- * Arguments IN: segment_number: number of segment to be set
- * visibility_status: nonzero means set to visible;
- * zero means set to invisible
- * Returns: nothing
- */
- lgd_set_segment_visibility( segment_number, visibility_status )
- int segment_number, visibility_status;
- {
- mfa address;
-
- /* Find the segment */
- address = segment_address( (mfe)segment_number );
- if (address==NULL_SEG_ADDRESS) {
- lgd_error = E_visi_noseg;
- return;
- }
- /* Change its visibility attribute in the MF */
- visibility_status = (visibility_status==0) ? INVISIBLE : VISIBLE;
- mf_poke( (mfa)(address+VIS_OS), (mfe)visibility_status );
- /* Tell the DPU to change the attribute too */
- dpu_set_segment_visibility( (mfe)segment_number, visibility_status );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU )",dpu_error);
- return;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_save_world
- * Description: Save the current world in a file
- * Arguments IN: fname: name of file to write to
- * Returns: nothing
- */
- lgd_save_world( fname )
- char *fname;
- {
- FILE *fp;
- int items_written;
-
- fp = fopen( fname, "w" );
- if (fp==NULL) {
- lgd_error = E_sw_bad_fname;
- return;
- }
- /* Write the format number: */
- items_written = fwrite( (char*)&lgd_world_file_fmt,
- sizeof(int), 1, fp );
- if (items_written!=1) {
- lgd_error = E_sw_bad_write;
- goto closeit;
- }
-
- lgd_save( fp );
- if (lgd_internal_error) {
- MAKE_LGD_ERR("",lgd_internal_error);
- goto closeit;
- }
- mf_save( fp );
- if (mf_error) {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- goto closeit;
- }
- dpu_save( fp );
- if (dpu_error) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- goto closeit;
- }
- closeit:
- fclose(fp);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_load_world
- * Description: load a new world from a file
- * Arguments IN: fname: name of file to read from
- * Returns: nothing
- * Notes: deletes the current world and all current settings
- * before loading new world
- */
- lgd_load_world( fname )
- char *fname;
- {
- FILE *fp;
- int items_read, format;
-
- fp = fopen( fname, "r" );
- if (fp==NULL) {
- lgd_error = E_lw_bad_fname;
- return;
- }
-
- /* Read the format number: */
- items_read = fread( (char*)&format,
- sizeof(int), 1, fp );
- if (items_read!=1) {
- lgd_error = E_lw_bad_read;
- goto closeit;
- }
- /* Make sure it agrees with the current format */
- if (format!=lgd_world_file_fmt) {
- lgd_error = E_lw_fmt_clash;
- goto closeit;
- }
-
- lgd_load( fp );
- if (lgd_internal_error) {
- MAKE_LGD_ERR("",lgd_internal_error);
- goto closeit;
- }
- mf_load( fp );
- if (mf_error) {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- goto closeit;
- }
- dpu_load( fp );
- if (dpu_error) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- goto closeit;
- }
- closeit:
- fclose(fp);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_color
- * Description: Set the current drawing color
- * Arguments IN: color: the color to use
- * Returns: nothing
- * Notes: Has no effect on monochrome displays. Color
- * information is retained in the MF, however, so a MF
- * generated on a monochrome device, when displayed on a
- * color device, will show the colors that were in
- * effect when it was created.
- */
- lgd_set_color( color )
- int color;
- {
- cur_color = (mfe)color;
- if (SEGMENT_OPEN) {
- mf_append( MF_color );
- mf_append( cur_color );
- if (mf_error!=NULL)
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
- dpu_set_color( cur_color );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_style
- * Description: Set the current line style
- * Arguments IN: style: the style to use
- * Returns: nothing
- * Notes: Has no effect on some displays. Style information is
- * retained in the MF, however, so a MF generated on a
- * style-less device, when displayed on one which shows
- * different styles, will show the styles that were in
- * effect when it was created.
- */
- lgd_set_style( style )
- int style;
- {
- cur_style = (mfe)style;
- if (SEGMENT_OPEN) {
- mf_append( MF_line_style );
- mf_append( cur_style );
- if (mf_error!=NULL)
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
- dpu_set_style( cur_style );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_view
- * Description: Set the viewing transformation
- * Arguments IN: *view: the new transformation
- * Returns: nothing
- * Notes: The view may not actually be changed by this call.
- * Call "lgd_update_display" to make sure the display
- * is current.
- */
- lgd_set_view( view )
- lgd_View3 *view;
- {
- dpu_set_view( view );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_get_default_view
- * Description: Get a standard default viewing transformation
- * Arguments OUT: *view: the transformation
- * Returns: nothing
- * Notes:
- */
- lgd_get_default_view( view )
- lgd_View3 *view;
- {
- dpu_get_default_view( view );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_view
- * Description: Get the current viewwing transformation
- * Arguments OUT: *view: the current transformation
- * Returns: nothing
- */
- lgd_inquire_view( view )
- lgd_View3 *view;
- {
- dpu_inquire_view( view );
- if (dpu_error!=NULL)
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_menu
- * Description: Set the menu to be used for user interaction
- * Arguments IN: *menu: the menu to use
- * Returns: nothing
- */
- lgd_set_menu( menu )
- lgd_Menu *menu;
- {
- dev_set_menu( menu );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_main_loop
- * Description: Enter the main user-interaction loop
- * Arguments: (none)
- * Returns: nothing
- */
- lgd_main_loop()
- {
- dev_main_loop();
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_end_loop
- * Description: Terminate the main loop
- * Arguments: (none)
- * Returns: nothing
- * Notes: The acceptable way to terminate is to call
- * lgd_end_loop from within one of the menu
- * procs. This will cause lgd_main_loop to return.
- */
- lgd_end_loop()
- {
- dev_end_loop();
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_get_string
- * Description: Get a string from the user via the keyboard
- * Arguments OUT: *s: string typed by the user
- * Returns: s
- * Notes: Characters are read up to and including a newline,
- * which is translated to the null character before
- * being stored in *s.
- */
- char *lgd_get_string( s )
- char *s;
- {
- dev_get_string( s );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_put_string
- * Description: Write a string to the user (on the screen or somewhere)
- * Arguments OUT: *s: string to be written
- * Returns: nothing
- */
- lgd_put_string( s )
- char *s;
- {
- dev_put_string( s );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_confirm
- * Description: Get confirmation (yes/no answer) from user
- * Arguments IN: *string: prompt to be printed
- * Returns: YES (1) or NO (0)
- */
- lgd_confirm( string )
- char *string;
- {
- int answer;
-
- answer = dev_confirm( string );
- return( answer );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_get_point
- * Description: Get a point in the current world from the user
- * Arguments OUT: v: the gotten point
- * Returns: nothing
- */
- lgd_get_point( v )
- double v[];
- {
- dev_get_point( v );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_color
- * Description: Tell what the current color is
- * Arguments OUT: *color: the current color
- * Returns: nothing
- */
- lgd_inquire_color( color )
- int *color;
- {
- *color = cur_color;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_style
- * Description: Tell what the current line style is
- * Arguments OUT: *style: the current style
- * Returns: nothing
- */
- lgd_inquire_style( style )
- int *style;
- {
- *style = cur_style;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_menu
- * Description: Tell what the current menu is
- * Arguments OUT: **menu: the current menu
- * Returns: nothing
- */
- lgd_inquire_menu( menu )
- lgd_Menu **menu;
- {
- dev_inquire_menu( menu );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_current_segment
- * Description: Tell what the current segment is
- * Arguments OUT: *segment: the current segment
- * Returns: nothing
- */
- lgd_inquire_current_segment( segment )
- int *segment;
- {
- *segment = cur_segment;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_segment_visibility
- * Description: Get the visibility attribute of a segment
- * Arguments IN: segment_number: number of segment
- * OUT: *visibility: 1=visible, 0=invisible
- * Returns: *visibility
- * Notes: Returns -1 and sets an error if segment does not
- * exist
- */
- lgd_inquire_segment_visibility( segment_number, visibility )
- int segment_number, *visibility;
- {
- mfa address;
-
- address = segment_address( (mfe)segment_number );
- if (address==NULL_SEG_ADDRESS) {
- lgd_error = E_inq_noseg_visi;
- return(-1);
- }
- *visibility = mf_peek( address+VIS_OS );
- return( *visibility );
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_pick_tolerance
- * Description: set the tolerance for picking operations
- * Arguments IN: t: the new tolerance
- * Returns: nothing
- * Notes: This has effect only on devices which are able to
- * implement picking.
- */
- lgd_set_pick_tolerance(t)
- double t;
- {
- lgd_pick_tolerance = t;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_inquire_pick_tolerance
- * Description: get the current pick tolerance
- * Arguments: OUT: *t: the current pick tolerance
- * Returns: nothing
- * Notes: This is meaningful only on devices which are able to
- * implement picking.
- */
- lgd_inquire_pick_tolerance(t)
- double *t;
- {
- *t = lgd_pick_tolerance;
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_pick_segment
- * Description: see dpu_pick_segment in dpu.c (part of device driver)
- */
- lgd_pick_segment( s, n )
- int **s, *n;
- {
- dpu_pick_segment(s, n);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_signal
- * Description: set a trap for a Unix signal
- * Args IN: sig: the signal to catch
- * func: the function to call when the signal is caught
- * Notes: LGD applications should call this instead of signal(3)
- * in order to peacefullly exist with lgd_main_loop().
- */
- lgd_signal(sig, func)
- int sig, (*func)();
- {
- dev_signal(sig, func);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_set_input_func
- * Description: specify a function to call when input is available from
- * a pipe
- * Args IN: func: the function to call
- * fd: file descriptor of pipe
- */
- lgd_set_input_func(func, fd)
- int (*func)(), fd;
- {
- dev_set_input_func(func, fd);
- }
-
- /*--------------------End of External Procedures------------------------*/
-
- /************************************************************************
- * INTERNAL PROCEDURES *
- ************************************************************************/
-
- /*-----------------------------------------------------------------------
- * Function: move_abs
- * Description: Move the current point to a new point
- * Arguments IN: v: the point to move to
- * Returns: nothing
- * Notes: v MUST be inside (or on the boundary of) the world.
- * If it is not, an error is set and the current point
- * is left unchanged.
- */
- static
- move_abs( v )
- double v[];
- {
- mfe ivec[MAXDIM];
-
- if NO_SEGMENT_OPEN {
- lgd_error = E_md_noseg;
- return;
- }
- if (in_world_box(v)) {
- pen_up();
- can_int_coord( ivec, v );
- mf_append_array( ivec, lgd_dim );
- if (mf_error==NULL) {
- LGD_copy_vec( cp, v );
- dpu_move( v );
- if (dpu_error!=NULL) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
- }
- else {
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
- }
- else {
- lgd_error = E_out_of_world;
- }
- }
-
- /*--------------------------------------------------------------
- * Function: draw_abs
- * Description: Draw from the current point to a new point, and
- * then make that new point the current point
- * Arguments IN: v: the point to draw to
- * Returns: nothing
- * Notes: v MUST be inside (or on the boundary of) the world.
- * If it is not, an error is set and the current point
- * is left unchanged.
- */
- static
- draw_abs( v )
- double v[];
- {
- mfe ivec[MAXDIM];
-
- if NO_SEGMENT_OPEN {
- lgd_error = E_md_noseg;
- return;
- }
- if (in_world_box(v)) {
- pen_down();
- can_int_coord( ivec, v );
- mf_append_array( ivec, lgd_dim );
- if (mf_error==NULL) {
- LGD_copy_vec( cp, v );
- dpu_draw( v );
- if (dpu_error!=NULL) {
- MAKE_LGD_ERR("(DPU) ",dpu_error);
- }
- }
- else
- MAKE_LGD_ERR("(MetaFile) ",mf_error);
- }
- else {
- lgd_error = E_out_of_world;
- }
- }
-
- /*-----------------------------------------------------------------------
- * Function: pen_up
- * Description: Raise the pen if it's not already up
- * Arguments: none
- * Returns: nothing
- */
- static
- pen_up()
- {
- if NO_SEGMENT_OPEN {
- lgd_internal_error = E_pu_noseg;
- return;
- }
- if PEN_IS_DOWN {
- mf_append( MF_pen_up );
- if (mf_error==NULL)
- pen_status = UP;
- else
- MAKE_LGD_INTERNAL_ERR("(MetaFile) ",mf_error);
- }
- }
-
- /*--------------------------------------------------------------
- * Function: pen_down
- * Description: Lower the pen if it's not already down
- * Arguments: none
- * Returns: nothing
- */
- static
- pen_down()
- {
- if NO_SEGMENT_OPEN {
- lgd_internal_error = E_pd_noseg;
- return;
- }
- if PEN_IS_UP {
- mf_append( MF_pen_down );
- if (mf_error==NULL)
- pen_status = DOWN;
- else
- MAKE_LGD_INTERNAL_ERR("(MetaFile) ",mf_error);
- }
- }
-
- /*-----------------------------------------------------------------------
- * Function: in_world_box
- * Description: Check to see if a point is in the world box or not.
- * Arguments IN: v: the point to check
- * Returns: YES or NO
- * Notes: On the boundary counts as IN.
- */
- static
- in_world_box( v )
- double v[];
- {
- int i, inbox;
-
- inbox = YES;
- i = 0;
- while ( (inbox) && (i<lgd_dim) ) {
- inbox = ( (lgd_wbox[0][i]<=v[i] )
- &&( v[i]<=lgd_wbox[1][i] ) );
- ++i;
- }
- return(inbox);
- }
-
- /*-----------------------------------------------------------------------
- * Function: can_int_coord
- * Description: Convert world coords to canonical integer coords
- * Arguments IN: v: vector in world coords to be converted
- * OUT: ivec: integer vector answer
- * Returns: nothing
- * Notes: This procedure ASSUMES that v is inside the world box.
- * If v is not in the world box, the results could be
- * disasterous.
- */
- static
- can_int_coord( ivec, v )
- mfe ivec[];
- double v[];
- {
- int i;
-
- for (i=0; i<lgd_dim; ++i)
- ivec[i] = (mfe) ( (v[i]-lgd_wbox[0][i]) * lgd_wscale[i] );
- }
-
- /*-----------------------------------------------------------------------
- * Function: segment_address
- * Description: Get the address of a segment in the MetaFile
- * Arguments IN: segment_number: number of segment to get address of
- * Returns: the address
- * Notes: Returns NULL_SEG_ADDRESS if the given segment doesn't
- * exist
- */
- static mfa
- segment_address( segment_number )
- mfe segment_number;
- {
- mfa address, last;
-
- last = mf_last_addr();
- for (address=mf_first_addr(); address<last; ++address)
- if (mf_peek(address)==MF_begin_segment)
- if (mf_peek(address+NAME_OS)==segment_number)
- return(address);
- return(NULL_SEG_ADDRESS);
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_save
- * Description: write values for module "lgd" to a file
- * Arguments IN: fp: the file to write to
- * Returns: nothing
- */
- static
- lgd_save( fp )
- FILE *fp;
- {
- int items_written;
-
- #define SAVEIT(x,type) {items_written = fwrite( \
- (char*)x,sizeof(type), 1, fp );\
- if (items_written!=1) \
- {lgd_internal_error=E_swd_bad_write; return; } }
- /*
- *********************
- * THIS IS FORMAT 3 *
- *********************
- */
- SAVEIT( &lgd_dim, int);
- SAVEIT( &lgd_header_length, int);
- SAVEIT( &lgd_trailer_length, int);
- SAVEIT( &pen_status, int);
- SAVEIT( cp, cp );
- SAVEIT( lgd_wbox, lgd_wbox );
- SAVEIT( lgd_wscale, lgd_wscale );
- SAVEIT( &cur_segment, mfe );
- SAVEIT( &cur_color, mfe );
- SAVEIT( &cur_style, mfe );
-
- #undef SAVEIT
- }
-
- /*-----------------------------------------------------------------------
- * Function: lgd_load
- * Description: read values for module "lgd" from a file
- * Arguments IN: fp: the file to read from
- * Returns: nothing
- */
- static
- lgd_load( fp )
- FILE *fp;
- {
- int items_read;
-
- #define LOADIT(x,type) {items_read = fread( \
- (char*)x,sizeof(type), 1, fp );\
- if (items_read!=1) \
- {lgd_internal_error=E_swd_bad_read; return; } }
-
- /*
- *********************
- * THIS IS FORMAT 3 *
- *********************
- */
-
- LOADIT( &lgd_dim, int);
- LOADIT( &lgd_header_length, int);
- LOADIT( &lgd_trailer_length, int);
- LOADIT( &pen_status, int);
- LOADIT( cp, cp );
- LGD_copy_vec(crp, cp);
- LOADIT( lgd_wbox, lgd_wbox );
- LOADIT( lgd_wscale, lgd_wscale );
- LOADIT( &cur_segment, mfe );
- LOADIT( &cur_color, mfe );
- LOADIT( &cur_style, mfe );
-
- #undef LOADIT
- }
-
- /*-----------------------------------------------------------------------
- * Function: generate_unique_segment_number
- * Description: get unique number to use for a new segment
- * Arguments: (none)
- * Returns: the number
- */
- static mfe
- generate_unique_segment_number()
- {
- mfa maxsegnum, address, last;
- mfe segnum;
- char *flags, *calloc();
-
- #define USED 'u'
- last = mf_last_addr();
- if (last==0) /* MF is empty */
- segnum = 1;
- else { /* MF is not empty */
- /* Get max possible seg number and seg up flags array: */
- maxsegnum = mf_length() / (lgd_header_length + lgd_trailer_length) - 1;
- segnum = NULL_SEGMENT;
- flags = calloc( (unsigned)(maxsegnum+1), sizeof(char) );
- /* Go through MF, marking used segment numbers in flags array: */
- for (address=mf_first_addr(); address<last; ++address) {
- if (mf_peek(address)==MF_begin_segment) {
- if ( (segnum=mf_peek(address+NAME_OS)) > maxsegnum ) {
- lgd_internal_error = E_max_segs;
- break;
- }
- else
- flags[segnum] = USED;
- }
- }
- if (segnum!=NULL_SEGMENT) {
- /* Now go thru flags array to find first unused number */
- for (segnum=1; segnum<=maxsegnum; ++segnum)
- if (flags[segnum]!=USED) break;
- if (segnum>maxsegnum) {
- lgd_internal_error = E_max_segs;
- segnum = NULL_SEGMENT;
- }
- }
- free(flags);
- }
- return(segnum);
- #undef USED 'u'
- }
-
- /*-----------------------------------------------------------------------
- * Function: error_string
- * Description: construct an error message string
- * Arguments IN: prefix: error prefix
- * message: the error message
- * Returns: nothing
- * Notes: The pointer points to internal storage space which
- * is reused on subsequent calls.
- */
- static char *
- error_string( prefix, message )
- char *prefix, *message;
- {
- static char ermsg[200];
-
- sprintf( ermsg, "%s%s", prefix, message );
- return( ermsg );
- }
-
- /*======================================================================
- *
- * CLIPPING STUFF
- *
- * This is an adaptation of the Cohen-Sutherland clipping algorithm,
- * from pages 148-149 of Foley & Van Dam's `Fundamentals of
- * Interactive Computer Graphics'. This version is modified in two
- * ways:
- * 1. It does 3-dimensional clipping
- * 2. It is designed to be used for translating one set of
- * move/draw commands (with no restrictions on the points
- * moved or drawn to) to another set of move/draw commands
- * in which the points are restricted to lie within the
- * world box.
- */
-
- typedef unsigned short int Outcode_int;
-
- typedef struct outcode_s {
- unsigned bit1 : 1;
- unsigned bit2 : 1;
- unsigned bit3 : 1;
- unsigned bit4 : 1;
- unsigned bit5 : 1;
- unsigned bit6 : 1;
- unsigned pad : BITSPERBYTE*sizeof(Outcode_int) - 6;
- } Outcode_struct;
-
- typedef union outcode_u {
- Outcode_int I;
- Outcode_struct S;
- } Outcode;
-
- #define TRIVIAL_REJECT(c1,c2) (c1.I&c2.I)
- #define TRIVIAL_ACCEPT(c1,c2) (!(c1.I|c2.I))
-
- #define SWAP(x1,y1,z1,x2,y2,z2,c1,c2,dtemp,ctemp) \
- dtemp = x1; x1 = x2; x2 = dtemp; \
- dtemp = y1; y1 = y2; y2 = dtemp; \
- dtemp = z1; z1 = z2; z2 = dtemp; \
- ctemp.I = c1.I; c1.I = c2.I; c2.I = ctemp.I;
-
- #define xmin lgd_wbox[0][0]
- #define ymin lgd_wbox[0][1]
- #define zmin lgd_wbox[0][2]
- #define xmax lgd_wbox[1][0]
- #define ymax lgd_wbox[1][1]
- #define zmax lgd_wbox[1][2]
-
- /*-----------------------------------------------------------------------
- * Function: outcodes
- * Description: compute the Outcode of a point
- * Arguments IN: x,y,z: the coords of the point
- * Returns: the Outcode (as its "Outcode_int" member)
- */
- static Outcode_int
- outcode(x, y, z)
- double x,y,z;
- {
- Outcode code;
-
- code.I = 0;
- code.S.bit1 = (z > zmax);
- code.S.bit2 = (z < zmin);
- code.S.bit3 = (y > ymax);
- code.S.bit4 = (y < ymin);
- code.S.bit5 = (x > xmax);
- code.S.bit6 = (x < xmin);
- return(code.I);
- }
-
- /*-----------------------------------------------------------------------
- * Function: clipit
- * Description: clips line segment to world box
- * Arguments IN: a: initial point of segment
- * b: terminal point of segment
- * OUT: a: initial point of clipped segment
- * b: terminal point of clipped segment
- * *clipa: flag telling whether point a was clipped
- * *accept: flag telling whether segment is visible
- * at all.
- * Returns: nothing
- * Notes: If visible==NO upon return, then the values of the
- * other 3 parameters are meaningless.
- */
- static
- clipit(a, b, clipa, accept)
- double a[], b[];
- int *clipa, *accept;
- {
- double x1, y1, z1, x2, y2, z2;
- double dtemp, factor;
- Outcode c1,c2,ctemp;
- int done, swapped, first_pass;
-
- x1 = a[0]; y1 = a[1]; z1 = a[2];
- x2 = b[0]; y2 = b[1]; z2 = b[2];
-
- done = NO;
- *accept = NO;
- swapped = NO;
- first_pass = YES;
- do {
- /* compute Outcodes */
- c1.I = outcode(x1, y1, z1);
- if (first_pass) {
- *clipa = (c1.I!=0); /* (Outcode nonzero --> pt is OUT) */
- first_pass = NO;
- }
- c2.I = outcode(x2, y2, z2);
- /* check for trivial rejection */
- if (TRIVIAL_REJECT(c1, c2))
- done = YES;
- else {
- /* check for trivial acceptance */
- if (*accept=TRIVIAL_ACCEPT(c1,c2))
- done = YES;
- else { /* Subdivide the line */
- /* First, swap if necessary to guarantee that point 1 is OUT */
- if (!c1.I) {
- SWAP(x1, y1, z1, x2, y2, z2, c1, c2, dtemp, ctemp);
- swapped = !swapped;
- }
- /* Now do the subdivision */
- if (c1.S.bit1) { /* z > zmax */
- factor = (zmax - z1) / (z2 - z1);
- x1 = x1 + (x2 - x1) * factor;
- y1 = y1 + (y2 - y1) * factor;
- z1 = zmax;
- }
- else if (c1.S.bit2) { /* z < zmin */
- factor = (zmin - z1) / (z2 - z1);
- x1 = x1 + (x2 - x1) * factor;
- y1 = y1 + (y2 - y1) * factor;
- z1 = zmin;
- }
- else if (c1.S.bit3) { /* y > ymax */
- factor = (ymax - y1) / (y2 - y1);
- x1 = x1 + (x2 - x1) * factor;
- z1 = z1 + (z2 - z1) * factor;
- y1 = ymax;
- }
- else if (c1.S.bit4) { /* y < ymin */
- factor = (ymin - y1) / (y2 - y1);
- x1 = x1 + (x2 - x1) * factor;
- z1 = z1 + (z2 - z1) * factor;
- y1 = ymin;
- }
- else if (c1.S.bit5) { /* x > xmax */
- factor = (xmax - x1) / (x2 - x1);
- y1 = y1 + (y2 - y1) * factor;
- z1 = z1 + (z2 - z1) * factor;
- x1 = xmax;
- }
- else if (c1.S.bit6) { /* x < xmin */
- factor = (xmax - x1) / (x2 - x1);
- y1 = y1 + (y2 - y1) * factor;
- z1 = z1 + (z2 - z1) * factor;
- x1 = xmin;
- }
- }
- }
- } while (!done);
-
- if (accept) {
- if (swapped) {
- a[0] = x2; a[1] = y2; a[2] = z2;
- b[0] = x1; b[1] = y1; b[2] = z1;
- }
- else {
- a[0] = x1; a[1] = y1; a[2] = z1;
- b[0] = x2; b[1] = y2; b[2] = z2;
- }
- }
- }
-